
package dovs.phases;

import dovs.*;
import dovs.analysis.*;
import dovs.node.*;
import dovs.instructions.*;

import java.util.*;

/** Compiler phase to generate Java bytecode for all
 *  method bodies.<p>
 */
public aspect CodeGeneration extends DepthFirstAdapter {
	/** The list of instructions generated for this body */
	public List<Instruction> ABody.instructions;

	/** The list of instructions for the body currently being generated */
	private List<Instruction> current_instrs;

	/** Puts an instruction into the current list */
	private void put(Instruction inst) {
		current_instrs.add(inst);
	}

	
	@Override 
	public void inABody(ABody body) {
		// Make list of instructions for method
		body.instructions = new LinkedList<Instruction>();
		current_instrs = body.instructions;
	}

	@Override @SuppressWarnings("unused")
	public void caseAFieldDecl(AFieldDecl fieldd) {
		// Inhibit direct traversal of field declarations
	}

	@Override 
	public void caseALocalDecl(ALocalDecl locald) {
		if (locald.getInit() != null) {
			// If the local variable being declared has an
			// initializer, generate code for it.
			locald.getInit().apply(this);
			
			// Store the result into the variable.
			switch (locald.getType().kindPType()) {
			case BYTE:
			case SHORT:
			case INT:
			case CHAR:
			case BOOLEAN:
				put(new Iistore(locald.index));
				break;
			case NAMED:
			case ARRAY:
				put(new Iastore(locald.index));
				break;
			default:
				throw new InternalCompilerError("Illegal type of local initializer");
			}
		}
	}

	@Override 
	public void caseAExpStm(AExpStm stm) {
		stm.getExp().apply(this);
		// A non-void expression statement leaves a
		// value on the stack. This value is not used
		// and so must be popped.
		switch (stm.getExp().type.kindPType()) {
		case VOID:
			break;
		default:
			put(new Ipop());
		}
	}
	

	@Override 
	public void caseAIntConstExp(AIntConstExp exp) {
		put(new Ildc_int(exp.value));
	}


	@Override 
	public void caseABooleanConstExp(ABooleanConstExp exp) {
		put(new Ildc_int(exp.value ? 1 : 0));
	}

	@Override @SuppressWarnings("unused")
	public void caseANullExp(ANullExp exp) {
		put(new Iaconst_null());
	}

	@Override @SuppressWarnings("unused")
	public void caseAThisExp(AThisExp exp) {
		put(new Iaload(0));
	}


	@Override 
	public void caseANonstaticInvokeExp(ANonstaticInvokeExp exp) {
		exp.getReceiver().apply(this);
		for (PExp arg : exp.getArgs()) {
			arg.apply(this);
		}
		if (exp.method_decl.parent() instanceof AInterfaceTypeDecl) {
			put(new Iinvokeinterface(new MethodSignature(exp.method_decl.getSignature())));
		} else {
			put(new Iinvokevirtual(new MethodSignature(exp.method_decl.getSignature())));
		}
	}

}
